相信經過前幾天的分享,你應該對大型語言模型的操作有更深入的認識,不過你可能也發現一點,就是每次都要自行設計 Prompt,而且還要去測試這個 Prompt 到底好不好,總讓人感覺有點不踏實啊… 有沒有什麼方法是這個 Prompt 有很高的機率很讚、而且可以去測試呢?
接下來我將重寫 Day6 的內容,讓這個會計算字串中英文字母的 LLM 系統更加「穩定」!希望他除了可以算一句話的 strawberry 有幾個 r ,也可以順便算 banana 有幾個 a,不熟悉的朋友可以回到 Day6 複習一下歐。
測試驅動開發 (TDD) 是一種軟體開發的方法,它強調在寫程式之前,先撰寫測試案例,以確保每一個功能都是可測試的而且符合需求。
TDD 的流程可以概括為三個部分,通常稱為「紅、綠、重構」循環
有一篇文章寫得很好,可以參考看看〈TDD (Test-Driven Development) 測試驅動開發(入門篇)〉
不過這種開發方法是針對確定性 (Deterministic) 的系統,對 LLM 應用來說,這種每次的輸出結果具有隨機性 (stochastic) 的系統,應該如何做 TDD 呢?
答案就是做評估 (Evaluation),評估需要準備測試資料集,以及對應的標準答案 (不一定要有),就是很多一問一答,讓 AI 生成答案,然後評估,算出目前的 Promt 可以達到多少準確率
評估方式有以下
通常我們會讓 AI 產生多個 Prompt,然後保留測試後準確率最高的 Prompt,接下來介紹 Prompt 產生工具以及自動測試 / 最佳化的工具,想看相關研究可以參考論文〈Large Language Models Are Human-Level Prompt Engineers〉
參考資料:ihower 大大的〈評估驅動開發: 生成式 AI 軟體不確定性的解決方法〉
以下我就來介紹一些 AI 產生 Prompt 的工具吧!我這次就不用 Day7 用的 OpenAI Tool use API 來噁心大家XD,一樣用自己定義的 <API>
token 來表示 function call 的地方,讓模型自行判斷要使用工具的地方。
先看我原本的 Prompt
你的任務是幫助使用者計算字母的數量。
在你要表達數字的地方用 <API>input_string, target_character</API> 來表達。
把 input_string 改成要計算的字串,target_character 改成目標字母。
注意:
1. 不要用引號來包住 input_string 和 target_character。
2. 不要在 input_string 和 target_character 之間加任何符號,例如逗號、空格或其他符號。
3. 不用額外說明計算過程,可以加上自然語言潤飾句子
4. 使用**繁體中文**回答問題。
我們來看看用以下這些工具,他會把我的 Prompt 改成什麼樣子吧!
首先是 Anthropic 的 prompt generator,這個 Anthropic 就是 Claude 模型的公司啦
Automatically generate first draft prompt templates 連結
如果你有訂閱 Claude,那可以直接從 API console 這邊找到 “Grenerate a prompt”
沒有訂閱的話也沒關係,這邊我使用 Anthropic 提供的 Colab,用 API key 去生成 prompt
填入 API key
到 Quickstart 這邊修改 TASK,改成你希望它產生的 Prompt 任務內容,你也自定變數 VARIABLES。
以下是我給他的 TASK prompt
你的任務是幫助使用者計算字母的數量。
你會自動把需要計算字數的地方用 <API>input_string,target_character</API> 來表達。
所以如果問的問題是 strawberry 有多少個 r
你的回答應該會是
strawberry 有 <API>strawberry,r</API> 個 r
把 input_string 改成要計算的字串,target_character 改成目標字母。
注意:
1. 不要用引號來包住 input_string 和 target_character。
2. 不要在 input_string 和 target_character 之間加任何符號,例如逗號、空格或其他符號。
3. 直接在要計算數字的地方用 <API> 不用額外說明計算過程,你就當這個 <API> 是一個數字即可
4. 可以加上自然語言潤飾句子,不要出現不合理的回覆
5. 使用**繁體中文**回答問題。
然後把程式把都執行一遍,最後的 prompt 長這樣
還有一件事,claude 生成的 Prompt 可能會習慣在答案加上一個 <Answer>
標籤,這部分我們不需要所以可以直接丟掉,如果你還有覺得哪邊寫得不好的也可以自行更改,像這邊我就希望他給的 Few-shot example 用雙引號來包住英文字串而不是上引號
真正最有效的 Prompt 其實是人的經驗 + AI 的知識~
以下是我修改後最終的 Prompt
你是一個協助計算字母數量的AI助手。你的任務是回答使用者關於特定字串中某個字母出現次數的問題。
當需要計算字母數量時,你應該使用以下格式:
<API>input_string,target_character</API>
這個API標籤會自動計算input_string中target_character的出現次數。你應該將這個標籤直接插入到你的回答中,就像它是一個數字一樣。
回答時,請遵循以下指示:
1. 使用繁體中文回答。
2. 直接在需要顯示計算結果的地方使用<API>標籤。
3. 不要解釋計算過程,只需呈現結果。
4. 確保你的回答聽起來自然且合理。
例子:
問題:apple 中有幾個 p ?
回答: "apple" 中有<API>apple,p</API>個"p"。
問題:butterfly裡的t出現了幾次?
回答:在"butterfly" 這個單詞中,t出現了<API>butterfly,t</API>次。
注意事項:
- 不要在<API>標籤內使用引號。
- 在input_string和target_character之間不要加入任何符號(如逗號或空格)。
- 不要額外解釋<API>標籤的功能,直接將它視為計算結果使用。
現在,請回答以下問題:
<question>{$USER_QUESTION}</question>
馬上來試試!這邊因為我 Day6 的程式碼的 format_string
只有考慮一組 <API>
的情況,所以我要改成看到 <API>
都要呼叫計算字數的函式
def format_string(ai_response_string):
while True:
start_index = ai_response_string.find("<API>") # 找到 <API> 的開始位置,如果找不到會回傳 -1
if start_index == -1:
break
end_index = ai_response_string.find("</API>", start_index) # 找到 </API> 的結束位置
if end_index == -1:
break
api_content = ai_response_string[start_index+5:end_index] # 取得 API 內容
input_string, target_character = api_content.split(',') # 分割 input_string 和 target_character
letter_count = calculate_letter_count(input_string.strip(), target_character.strip()) # 計算字母數量
ai_response_string = ai_response_string[:start_index] + str(letter_count) + ai_response_string[end_index+6:] # 把計算結果放回 ai_response_string 中
return ai_response_string
測試結果如下,效果超讚ㄉ (模型選用 gpt-4o-mini
)
怕篇幅太長,我把程式碼放在 Github 上了,你也可以直接複製出來玩玩看歐
還有其他酷酷的 Prompt 產生器,用法都一樣,跟他說你要叫這個 AI 做什麼任務,他就幫你產生 Prompt ㄌ,不過要記得 AI 生的 Prompt 不見得是最好的,還是要測試、精練 Prompt
不過目前最好用的應該還是 Anthropic Claude
剛剛有提到評估模型需要用到測試資料集,那測試案例怎麼來?一樣可以用 LLM 幫我們生成資料。聽到這邊你可能想說,又要去複製貼上了…No no no,已經有現成的工具了,而且它幫你把 Prompt 生成到資料評估都做完了,甚至寫成一個 Colab 筆記本,直接使用。
這個工具就是 gpt-prompt-engineer,我們直接下載他的 ipynb 檔案,並到 Colab 上使用。
以剛剛計算英文字數的例子,來寫 Prompt,我們需要修改的有 “In the cell below, fill in your description and test cases” 這邊。
description = """
你是一個協助計算字母數量的AI助手。你的任務是回答使用者關於特定字串中某個字母出現次數的問題。
當需要計算字母數量時,你應該使用以下格式:
<API>input_string,target_character</API>
這個API標籤會自動計算input_string中target_character的出現次數。你應該將這個標籤直接插入到你的回答中,就像它是一個數字一樣。
回答時,請遵循以下指示:
1. 使用繁體中文回答。
2. 直接在需要顯示計算結果的地方使用<API>標籤。
3. 不要解釋計算過程,只需呈現結果。
4. 確保你的回答聽起來自然且合理。
例子:
問題:apple 中有幾個 p ?
回答: "apple" 中有<API>apple,p</API>個"p"。
問題:butterfly裡的t出現了幾次?
回答:在"butterfly" 這個單詞中,t出現了<API>butterfly,t</API>次。
注意事項:
- 不要在<API>標籤內使用引號。
- 在input_string和target_character之間不要加入任何符號(如逗號或空格)。
- 不要額外解釋<API>標籤的功能,直接將它視為計算結果使用。
現在,請回答以下問題:
""" # this style of description tends to work well
test_cases = [
{
'prompt': '請問 banana 有幾個 n',
},
{
'prompt': '請問 strawberry 有幾個 r',
},
{
'prompt': '請問 strawberry 有幾個 r?請問 Banana 有幾個 a? 合併再一起有幾個 a',
}
]
if use_wandb:
wandb.config.update({"description": description,
"test_cases": test_cases})
如果你想要的話,也可以去改 CANDIDATE_MODEL
, NUMBER_OF_PROMPTS
等等參數,因為我希望他跑快一點所以這邊就把 NUMBER_OF_PROMPTS
設成 3,畢竟是簡單的任務
執行後他會幫你整理出這三個它產生的 Prompt 的分數 (因為我們設定 3 個 Prompt),再根據需要選出最大就好,只是格式有點醜就是了XD
測試看看吧!程式碼一樣放在 github 上
效果也很讚~
其他類似的工具還有:DSPy, AutoPrompt, Textgrad
本篇受 ihower 大大的演講內容的啟發,EDD 讓 LLM 開發的過程從充滿不確定到趨向穩定,真的是很有趣,對我來說也是很新的領域,之後在開發大型語言模型應用的時候也應該要把測試加進去才行。我覺得他有一點說得很好,就是一開始做 POC 大家都覺得很神奇,很厲害,但真的要做 Product 的時候才發現問題好像也蠻多的… 真的很有感!
明天我們終於要稍微脫離程式碼了,我會介紹 Dify / Langflow 等圖形化 LLM 工作流開發工具,期待一下吧!!!